package com.njlime.sys.security.util;

import com.njlime.core.helper.GlobalValue;
import com.njlime.core.helper.SpringContextHolder;
import com.njlime.sys.entities.*;
import com.njlime.sys.repository.*;
import com.njlime.sys.security.SystemAuthorizingRealm.Principal;
import com.njlime.sys.security.entity.LoginFail;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.UnavailableSecurityManagerException;
import org.apache.shiro.session.InvalidSessionException;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 用户工具类
 *
 * @author Dean
 * @version 1.0
 */
public class UserUtils {

    private static UserDao userDao = SpringContextHolder.getBean(UserDao.class);
    private static RoleDao roleDao = SpringContextHolder.getBean(RoleDao.class);
    private static MenuDao menuDao = SpringContextHolder.getBean(MenuDao.class);
    private static UnitDao unitDao = SpringContextHolder.getBean(UnitDao.class);
    private static UnitWxConfigDao unitWxConfigDao = SpringContextHolder.getBean(UnitWxConfigDao.class);
    public static final String USER_CACHE = "userCache";
    public static final String UNIT_CONFIG_CACHE = "unitConfigCache";
    public static final String USER_CACHE_ID_ = "id_";
    public static final String USER_CACHE_LOGIN_NAME_ = "ln_";
    public static final String USER_CACHE_LIST_BY_UNIT_ID_ = "uid_";

    public static final String CACHE_AUTH_INFO_ = "authInfo_";
    public static final String CACHE_ROLE_LIST_ = "roleList_";
    public static final String CACHE_MENU_LIST_ = "menuList_";
    public static final String CACHE_UNIT_LIST_ = "unitList_";

    public static final String CACHE_PUBLIC_MENU = "public_menu";

    /**
     * 根据单位id获取单位微信公共号
     *
     * @param unitId
     * @return 取不到返回null
     */
    public static UnitWxConfig getUnitConfig(int unitId) {
        UnitWxConfig config = (UnitWxConfig) CacheUtils.get(UNIT_CONFIG_CACHE, USER_CACHE_ID_ + unitId);
        if (config == null) {
            config = unitWxConfigDao.getWxConfig(unitId);
            if (config == null) {
                return null;
            }

            CacheUtils.put(UNIT_CONFIG_CACHE, USER_CACHE_ID_ + unitId, config);
        }
        return config;
    }

    /**
     * 根据ID获取用户
     *
     * @param id
     * @return 取不到返回null
     */
    public static User get(String id) {
        User user = (User) CacheUtils.get(USER_CACHE, USER_CACHE_ID_ + id);
        if (user == null) {
            user = userDao.get(Integer.parseInt(id));
            if (user == null) {
                return null;
            }
            user.setRoleList(roleDao.listByUserId(user.getId()));
            user.setUnit(unitDao.findOne(user.getUnit_id()));
            //user设置根单位，顶级单位返回null，二级单位返回本身，三级以下单位返回自身所属二级单位
            String[] units = user.getUnit().getParent_ids().replace(" ","").split(",");
            if(units.length >= 3){
                user.setRootUnit(unitDao.findOne(Integer.parseInt(units[2])));
            }else if(units.length == 2){
                user.setRootUnit(user.getUnit());
            }
            CacheUtils.put(USER_CACHE, USER_CACHE_ID_ + user.getId(), user);
            CacheUtils.put(USER_CACHE, USER_CACHE_LOGIN_NAME_ + user.getLogin_name(), user);
        }
        return user;
    }

    /**
     * 根据登录名获取用户
     *
     * @param loginName
     * @return 取不到返回null
     */
    public static User getByLoginName(String loginName) {
        User user = (User) CacheUtils.get(USER_CACHE, USER_CACHE_LOGIN_NAME_ + loginName);
        if (user == null) {
            user = userDao.getByLoginName(loginName);
            if (user == null) {
                return null;
            }
            user.setRoleList(roleDao.listByUserId(user.getId()));
            user.setUnit(unitDao.findOne(user.getUnit_id()));
            //user设置根单位，顶级单位返回null，二级单位返回本身，三级以下单位返回自身所属二级单位
            String[] units = user.getUnit().getParent_ids().replace(" ","").split(",");
            if(units.length >= 3){
                user.setRootUnit(unitDao.findOne(Integer.parseInt(units[2])));
            }else if(units.length == 2){
                user.setRootUnit(user.getUnit());
            }
            CacheUtils.put(USER_CACHE, USER_CACHE_ID_ + user.getId(), user);
            CacheUtils.put(USER_CACHE, USER_CACHE_LOGIN_NAME_ + user.getLogin_name(), user);
        }
        return user;
    }

    /**
     * 清除当前用户缓存
     */
    public static void clearCache() {
        User user = getUser();
        CacheUtils.remove(CACHE_AUTH_INFO_ + user.getId());
        CacheUtils.remove(USER_CACHE, CACHE_ROLE_LIST_ + user.getId());
        CacheUtils.remove(USER_CACHE, CACHE_MENU_LIST_ + user.getId());
        CacheUtils.remove(USER_CACHE, CACHE_UNIT_LIST_ + user.getId());
        UserUtils.clearCache(user);
    }

    /**
     * 清除指定用户缓存
     *
     * @param user
     */
    public static void clearCache(User user) {
        CacheUtils.remove(USER_CACHE, USER_CACHE_ID_ + user.getId());
        CacheUtils.remove(USER_CACHE, USER_CACHE_LOGIN_NAME_ + user.getLogin_name());
        if (user.getUnit_id() != null) {
            CacheUtils.remove(USER_CACHE, USER_CACHE_LIST_BY_UNIT_ID_ + user.getUnit_id());
        }
    }

    /**
     * 获取当前用户
     *
     * @return 取不到返回 new User()
     */
    public static User getUser() {
        Principal principal = getPrincipal();
        if (principal != null) {
            User user = get(principal.getId());
            if (user != null) {
                return user;
            }
            return new User();
        }
        // 如果没有登录，则返回实例化空的User对象。
        return new User();
    }

    /**
     * 获取当前用户角色列表
     *
     * @return
     */
    public static List<Role> getRoleList() {
        @SuppressWarnings("unchecked")
        User user = getUser();
        List<Role> roleList = (List<Role>) CacheUtils.get(USER_CACHE, CACHE_ROLE_LIST_ + user.getId());
        if (roleList == null) {
            roleList = roleDao.listByUserId(user.getId());
            for(Role role : roleList){
                role.setUnit(unitDao.findOne(role.getUnit_id()));
            }
            CacheUtils.put(USER_CACHE, CACHE_ROLE_LIST_ + user.getId(), roleList);
        }
        return roleList;
    }

    /**
     * 获取当前用户授权菜单
     *
     * @return
     */
    public static List<Menu> getMenuList() {
        @SuppressWarnings("unchecked")
        User user = getUser();
        List<Menu> menuList = (List<Menu>) CacheUtils.get(USER_CACHE, CACHE_MENU_LIST_ + user.getId());
        if (menuList == null) {
            if (isAdmin(user)) {
                menuList = menuDao.findAllList();
            } else {
                menuList = menuDao.listByUserId(user.getId());
            }
            CacheUtils.put(USER_CACHE, CACHE_MENU_LIST_ + user.getId(), menuList);
         }
        return menuList;
    }

    /**
     * 获取当前用户有权限访问的部门
     *
     * @return
     */
    public static List<Unit> getUnitList() {
        @SuppressWarnings("unchecked")
        User user = getUser();
        List<Unit> unitList = (List<Unit>) CacheUtils.get(USER_CACHE, CACHE_UNIT_LIST_ + user.getId());
        if (unitList == null) {
            if (isAdmin(user)) {
                unitList = unitDao.findAllList(null);
            } else {
                unitList = unitDao.listByUser(user);
            }
            CacheUtils.put(USER_CACHE, CACHE_UNIT_LIST_ + user.getId(), unitList);
        }
        return unitList;
    }

    /**
     * 缓存公共权限白名单
     * @return
     */
    public static List<Menu> getPublicMenuList(){
        List<Menu> menuList = (List<Menu>) CacheUtils.get(USER_CACHE, CACHE_PUBLIC_MENU);
        if(menuList == null){
            menuList = menuDao.getMenuChildList(GlobalValue.PUBLIC_MENU_ID);
            CacheUtils.put(USER_CACHE, CACHE_PUBLIC_MENU, menuList);
        }
        return menuList;
    }

    private static boolean isAdmin(User user) {
        Integer id = user.getId();
        return id != null && GlobalValue.SYS_USER_ID.equals(id);
    }

    /**
     * 获取授权主要对象
     */
    public static Subject getSubject() {
        return SecurityUtils.getSubject();
    }

    /**
     * 获取当前登录者对象
     */
    public static Principal getPrincipal() {
        try {
            Subject subject = SecurityUtils.getSubject();
            Principal principal = (Principal) subject.getPrincipal();
            if (principal != null) {
                return principal;
            }
        } catch (UnavailableSecurityManagerException e) {

        } catch (InvalidSessionException e) {

        }
        return null;
    }

    public static Session getSession() {
        try {
            Subject subject = SecurityUtils.getSubject();
            Session session = subject.getSession(false);
            if (session == null) {
                session = subject.getSession();
            }
            if (session != null) {
                return session;
            }
        } catch (InvalidSessionException e) {

        }
        return null;
    }

    // ============== User Session ==============

    public static Object getCache(String key) {
        return getCache(key, null);
    }

    public static Object getCache(String key, Object defaultValue) {
        Object obj = getSession().getAttribute(key);
        return obj == null ? defaultValue : obj;
    }

    public static void putCache(String key, Object value) {
        getSession().setAttribute(key, value);
    }

    public static void removeCache(String key) {
        getSession().removeAttribute(key);
    }

    //=================login Fail======================

    /**
     * 登录失败次数方法
     * @param username 用户名
     * @param isFail   是否失败
     * @param clean    是否清空
     */
    public static void loginFail(String username, boolean isFail, boolean clean) {
        Map<String, LoginFail> loginFailMap = (Map<String, LoginFail>) CacheUtils.get("loginFailMap");
        if (loginFailMap == null) {
            loginFailMap = new HashMap<>();
            CacheUtils.put("loginFailMap", loginFailMap);
        }
        LoginFail loginFail = loginFailMap.get(username);
        if (loginFail == null) {
            loginFail = new LoginFail();
        }
        if (isFail) {
            if(loginFail.getLoginFailCount() < GlobalValue.MAX_LOGIN_FAIL_TIMES){
                loginFail.setLoginFailCount(loginFail.getLoginFailCount() + 1);
                loginFail.setLastFailTime(System.currentTimeMillis());
                loginFailMap.put(username, loginFail);
            }
        }
        if (clean) {
            loginFailMap.remove(username);
        }
    }
}
